﻿using System;
using System.Collections.Specialized;
using Hopscotch.Domain.Common;
using Hopscotch.Presentation.Common.Threading;

namespace Hopscotch.Presentation.Common.Collections
{

    /// <summary>
    /// An INotifyCollection that dispatches all operations to the UI queue
    /// This is needed to ensure the CollectionChanged event is run by the dispather thread as required by WPF,
    /// but also ensures there are no race conditions where items are added/removed while the UI is still reading the collection.
    /// To minimize effort on the Dispatcher thread, all operations are O(n), do any heavy lifting on a background thread before adding to the collection
    /// </summary>
    public class DispatcherCollection<T, TKey> : EntityCollection<T, TKey>, IEntityCollection<T, TKey>, INotifyCollectionChanged//, INotifyPropertyChanged
        where T : DomainBase<TKey>
        where TKey : struct
    {

        private readonly IDispatcher dispatcher;
        public event NotifyCollectionChangedEventHandler CollectionChanged;

        public DispatcherCollection(IDispatcher dispatcher)
        {
            this.dispatcher = dispatcher;
        }


        private void Dispatch(Action action)
        {
            if (dispatcher == null || dispatcher.CheckAccess())
            {
                //No dispatch required, run on the caller thread;
                action.Invoke();
            }
            else
            {
                dispatcher.RunAsync(action);
            }
        }

        public void Merge(T item)
        {
            Dispatch(() =>
                {
                    var key = GetKeyForItem(item);

                    if (ContainsKey(key))
                    {
                        //Merge
                        this[key].Merge(item);
                    }
                    else
                    {
                        //Add
                        base.Add(item);
                    }
                });
        }

        public override void Add(T item)
        {
            Dispatch(() => base.Add(item));
        }

        public override void Remove(TKey key)
        {
            Dispatch(() => base.Remove(key));
        }

        public override bool Remove(T item)
        {
            //throw System.NotSupportedException?
            Dispatch(() => base.Remove(item));

            return false;
        }

        public override void Clear()
        {
            Dispatch(() =>
            {
                base.Clear();
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            });

        }

        //protected override void SetItem(int index, T item)
        //{
        //    //DispatchInvoke(() =>
        //    //{
        //        base.SetItem(index, item);
        //        //OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, index));
        //    //});
        //}

        protected override void InsertItem(int index, T item)
        {
            base.InsertItem(index, item);
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
        }


        protected override void RemoveItem(int index)
        {
            T item = this[index];
            base.RemoveItem(index);

            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
        }


        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            var handler = CollectionChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
    }
}
